// bdlsb_memoutstreambuf.h                                            -*-C++-*-
#ifndef INCLUDED_BDLSB_MEMOUTSTREAMBUF
#define INCLUDED_BDLSB_MEMOUTSTREAMBUF

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide an output 'basic_streambuf' using managed memory.
//
//@CLASSES:
//   bdlsb::MemOutStreamBuf: output stream buffer using memory allocator
//
//@SEE_ALSO: bdlsb_fixedmemoutstreambuf, bdlsb_fixedmeminstreambuf
//
//@DESCRIPTION: This component provides a mechanism, 'bdlsb::MemOutStreamBuf',
// that implements the output portion of the 'bsl::basic_streambuf' protocol
// using a managed, allocator-supplied memory buffer.  Method names necessarily
// correspond to those specified by the protocol.
//
// This component provides none of the input-related functionality of
// 'basic_streambuf' (see "Streaming Architecture", below), nor does it use
// locales in any way.
//
// Because the underlying buffer is always obtained from the client-specified
// allocator, the 'pubsetbuf' method in this component has no effect.
//
// Note that this component has an unspecified minimum allocation size, and
// therefore users trying to limit themselves to a fixed buffer should use
// bdlsb_fixedmemoutstreambuf.
//
///Streaming Architecture
///----------------------
// Stream buffers are designed to decouple device handling from content
// formatting, providing the requisite device handling and possible buffering
// services, and leaving the formatting to the client stream.  The standard C++
// IOStreams library further partitions streaming into input streaming and
// output streaming, separating responsibilities for each at both the stream
// layer and the stream buffer layer.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
/// Example 1: Basic Use of 'bdlsb::MemOutStreamBuf'
///- - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates using a 'bdlsb::MemOutStreamBuf' in order to test
// a user defined stream type, 'CapitalizingStream'.  In this example, we'll
// define a simple example stream type 'CapitalizingStream' that capitalizing
// lower-case ASCII data written to the stream.  In order to test this
// 'CapitalizingStream' type, we'll create an instance, and supply it a
// 'bdlsb::MemOutStreamBuf' object as its stream buffer; after we write some
// character data to the 'CapitalizingStream' we'll inspect the buffer of the
// 'bdlsb::MemOutStreamBuf' and verify its contents match our expected output.
// Note that to simplify the example, we do not include the functions for
// streaming non-character data, e.g., numeric values.
//
// First, we define our example stream class, 'CapitalizingStream' (which we
// will later test using 'bdlsb::MemOutStreamBuf):
//..
//  class CapitalizingStream {
//      // This class capitalizes lower-case ASCII characters that are output.
//
//      // DATA
//      bsl::streambuf  *d_streamBuffer_p;   // pointer to a stream buffer
//
//      // FRIENDS
//      friend CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                            const char          *data);
//    public:
//      // CREATORS
//      explicit CapitalizingStream(bsl::streambuf *streamBuffer);
//          // Create a capitalizing stream using the specified 'streamBuffer'
//          // as underlying stream buffer to the stream.
//  };
//
//  // FREE OPERATORS
//  CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                 const char          *data);
//      // Write the specified 'data' in capitalized form to the specified
//      // 'stream'.
//
//  CapitalizingStream::CapitalizingStream(bsl::streambuf *streamBuffer)
//  : d_streamBuffer_p(streamBuffer)
//  {
//  }
//..
// As is typical, the streaming operators are made friends of the class.
//
// Note that we cannot directly use 'bsl::toupper' to capitalize each
// individual character, because 'bsl::toupper' operates on 'int' instead of
// 'char'.  Instead, we call a function 'ucharToUpper' that works in terms of
// 'unsigned char'.  some care must be made to avoid undefined and
// implementation-specific behavior during the conversions to and from 'int'.
// Therefore we wrap 'bsl::toupper' in an interface that works in terms of
// 'unsigned char':
//..
//  static unsigned char ucharToUpper(unsigned char input)
//      // Return the upper-case equivalent to the specified 'input' character.
//  {
//      return static_cast<unsigned char>(bsl::toupper(input));
//  }
//..
// Finally, we use the 'transform' algorithm to convert lower-case characters
// to upper-case.
//..
//  // FREE OPERATORS
//  CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                 const char          *data)
//  {
//      bsl::string tmp(data);
//      bsl::transform(tmp.begin(),
//                     tmp.end(),
//                     tmp.begin(),
//                     ucharToUpper);
//      stream.d_streamBuffer_p->sputn(tmp.data(), tmp.length());
//      return stream;
//  }
//..
// Now, we create an instance of 'bdlsb::MemOutStreamBuf' that will serve as
// underlying stream buffer for our 'CapitalingStream':
//..
//  bdlsb::MemOutStreamBuf streamBuffer;
//..
// Now, we test our 'CapitalingStream' by supplying the created instance of
// 'bdlsb::MemOutStreamBuf' and using it to inspect the output of the stream:
//..
//  CapitalizingStream  testStream(&streamBuffer);
//  testStream << "Hello world.";
//..
// Finally, we verify that the streamed data has been capitalized and placed
// into dynamically allocated buffer:
//..
//  assert(12 == streamBuffer.length());
//  assert(0  == bsl::strncmp("HELLO WORLD.",
//                            streamBuffer.data(),
//                            streamBuffer.length()));
//..

#include <bdlscm_version.h>

#include <bslma_allocator.h>
#include <bslma_default.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_types.h>

#include <bsl_cstddef.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>
#include <bsl_ios.h>
#include <bsl_streambuf.h>  // (char|int|pos|off|traits)_type

namespace BloombergLP {
namespace bdlsb {

                         // =====================
                         // class MemOutStreamBuf
                         // =====================

class MemOutStreamBuf : public bsl::streambuf {
    // This 'class' implements the output functionality of the
    // 'basic_streambuf' protocol, using a user-supplied or default 'bslma'
    // allocator to supply memory.

    // PRIVATE CONSTANTS
    enum {
        k_INITIAL_BUFFER_SIZE = 256,  // default initial buffer size

        k_GROWTH_FACTOR       =   2   // geometric growth factor to use when
                                      // resizing internal buffer

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BDESB_INITIAL_BUFFER_SIZE = k_INITIAL_BUFFER_SIZE
      , BDESB_GROWTH_FACTOR       = k_GROWTH_FACTOR
      , INITIAL_BUFFER_SIZE       = k_INITIAL_BUFFER_SIZE
      , GROWTH_FACTOR             = k_GROWTH_FACTOR
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

    // DATA
    bslma::Allocator *d_allocator_p;  // memory source for buffer memory
                                      // (held, not owned)

  private:
    // NOT IMPLEMENTED
    MemOutStreamBuf(const MemOutStreamBuf&); // = delete;
    MemOutStreamBuf& operator=(const MemOutStreamBuf&); // = delete;

  private:
    // PRIVATE MANIPULATORS
    void grow(bsl::size_t newLength);
        // Grow the size of the internal buffer to be at least large enough to
        // fit the specified 'newLength' characters.  The buffer size is grown
        // by the minimum power of 'k_GROWTH_FACTOR' needed to accommodate the
        // new length, but with a final size not less than
        // 'k_INITIAL_BUFFER_SIZE'.  This method has no effect if 'newLength <=
        // capacity()' holds before the call.

  protected:
    // PROTECTED MANIPULATORS
    virtual int_type overflow(
                  int_type insertionChar = bsl::streambuf::traits_type::eof());
        // Append the optionally specified 'insertionChar' to this stream
        // buffer's character buffer and return 'insertionChar'.  If
        // 'insertionChar' is not specified, 'traits_type::eof()' is appended
        // instead.

    virtual pos_type seekoff(off_type                offset,
                             bsl::ios_base::seekdir  way,
                             bsl::ios_base::openmode which = bsl::ios_base::in
                                                         | bsl::ios_base::out);
        // Set the position indicator to the relative specified 'offset' from
        // the base position indicated by the specified 'way' and return the
        // resulting absolute position on success or pos_type(-1) on failure.
        // Optionally specify 'which' area of the stream buffer.  The seek
        // operation will fail if 'which' does not include the flag
        // 'bsl::ios_base::out' or if the resulting absolute position is less
        // than zero or greater then 'length()'.

    virtual pos_type seekpos(pos_type                position,
                             bsl::ios_base::openmode which = bsl::ios_base::in
                                                         | bsl::ios_base::out);
        // Set the position indicator to the specified 'position' and return
        // the resulting absolute position on success or pos_type(-1) on
        // failure.  Optionally specify 'which' area of the stream buffer.  The
        // 'seekpos' operation will fail if 'which' does not include the flag
        // 'bsl::ios_base::out' or if 'position' is less then zero or greater
        // than 'length()'.

    virtual bsl::streamsize xsputn(const char_type *source,
                                   bsl::streamsize  numChars);
        // Write the specified 'numChars' characters from the specified
        // 'source' to the stream buffer.  Return the number of characters
        // successfully written.  The behavior is undefined unless '(source &&
        // 0 < numChars) || 0 == numChars'.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(MemOutStreamBuf,
                                   bslma::UsesBslmaAllocator);

    // CREATORS
    explicit
    MemOutStreamBuf(bslma::Allocator *basicAllocator = 0);
        // Create an empty stream buffer.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    explicit
    MemOutStreamBuf(bsl::size_t       numElements,
                    bslma::Allocator *basicAllocator = 0);
        // Create an empty stream buffer with sufficient initial capacity to
        // accommodate up to the specified 'numElements' characters without
        // subsequent reallocation.  If 'numElements == 0', an implementation-
        // defined initial capacity is used.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    ~MemOutStreamBuf();
        // Destroy this stream buffer.

    // MANIPULATORS
    void reserveCapacity(bsl::size_t numCharacters);
        // Reserve sufficient internal capacity to store at least the specified
        // 'numCharacters' characters without reallocation.  Note that if the
        // storage size specified is less than the number of characters already
        // in the buffer, this method has no effect.

    void reset();
        // Destroy the contents of this stream buffer, return all allocated
        // memory to the allocator, and reset the buffer to the default
        // constructed state.  Note that 'length() == 0' holds following a call
        // to this method.

    // ACCESSORS
    bsl::size_t capacity() const;
        // Return the current capacity of the buffer managed by this stream
        // buffer.

    const char *data() const;
        // Return the address of the non-modifiable character buffer managed by
        // this stream buffer.

    bsl::size_t length() const;
        // Return the number of valid characters in this stream buffer.
};

// ============================================================================
//                           INLINE DEFINITIONS
// ============================================================================

                         // ---------------------
                         // class MemOutStreamBuf
                         // ---------------------

// CREATORS
inline
MemOutStreamBuf::MemOutStreamBuf(bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    setp(0, 0);
}

inline
MemOutStreamBuf::MemOutStreamBuf(bsl::size_t       numElements,
                                 bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    setp(0, 0);
    reserveCapacity(numElements == 0
                    ? static_cast<bsl::streamsize>(k_INITIAL_BUFFER_SIZE)
                    : numElements);
}

inline
MemOutStreamBuf::~MemOutStreamBuf()
{
    d_allocator_p->deallocate(pbase());
}

// MANIPULATORS
inline
void MemOutStreamBuf::reset()
{
    d_allocator_p->deallocate(pbase());
    setp(0, 0);
}

// ACCESSORS
inline
bsl::size_t MemOutStreamBuf::capacity() const
{
    return epptr() - pbase();
}

inline
const char *MemOutStreamBuf::data() const
{
    return pbase();
}

inline
bsl::size_t MemOutStreamBuf::length() const
{
    return pptr() - pbase();
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
